// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
#pragma once
#include "LatLon.h"
#include "WeatherData.h"

namespace Weathr
{
    ref class Forecast;
    ref class ForecastGroup;

    [Windows::Foundation::Metadata::WebHostHidden]
    public delegate void WeatherDataChangedHandler(Forecast^ forecast);

    [Windows::UI::Xaml::Data::Bindable]
    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class Forecast sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged
    {
    public:
        virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
        static event WeatherDataChangedHandler^ WeatherDataChanged;

        // A unique identifier for placing the forecast in collections.
        property uint32 Uid
        {
            uint32 get() { return m_uid; }
        }

        property bool IsPending
        {
            bool get() { return m_pending; }
            void set(bool value)
            {
                if (value != m_pending)
                {
                    m_pending = value; OnPropertyChanged("IsPending");
                }
            }
        }

        property bool UpdateFailed
        {
            bool get() { return m_updateFailed; }
            void set(bool value)
            {
                if (value != m_updateFailed)
                {
                    m_updateFailed = value; OnPropertyChanged("UpdateFailed");
                }
            }
        }

        property WeatherData^ Current
        {
            WeatherData^ get() { return m_current; }
            void set(WeatherData^ value)
            {
                if (m_current != value)
                {
                    m_current = value;
                    OnPropertyChanged("Current");
                    OnPropertyChanged("HasCurrent");
                    WeatherDataChanged(this);
                }
            }
        }

        property bool HasCurrent
        {
            bool get() { return m_current != nullptr; }
        }

        property WeatherData^ Today
        {
            WeatherData^ get()
            {
                auto elem = std::min_element(begin(m_data), end(m_data), [](WeatherData^ data1, WeatherData^ data2)
                {
                    return data1->Date.UniversalTime < data2->Date.UniversalTime;
                });
                return elem != end(m_data) ? *elem : nullptr;
            }
        }

        // The name as specified in a data file or REST call.
        property Platform::String^ Locality
        {
            Platform::String^ get()
            {
                return m_locality;
            }
            void set(Platform::String^ value)
            {
                if (value != m_locality)
                {
                    m_locality = value; OnPropertyChanged("Locality");
                }
                if (m_displayName == nullptr || m_displayName->Length() == 0)
                {
                    DisplayName = m_locality;
                }
            }
        }

        // The name that's displayed to the user.
        property Platform::String^ DisplayName
        {
            Platform::String^ get()
            {
                return m_displayName;
            }
            void set(Platform::String^ value)
            {
                if (value != m_displayName)
                {
                    m_displayName = value; OnPropertyChanged("DisplayName");
                }
            }
        }

        property bool IsVisible
        {
            bool get() { return m_visible; }
            void set(bool value)
            {
                if (value != m_visible)
                {
                    m_visible = value; OnPropertyChanged("IsVisible");
                }
            }
        }

        property int32 ScreenLocationTop
        {
            int32 get() { return m_screenLocationTop; }
            void set(int32 value)
            {
                if (value != m_screenLocationTop)
                {
                    m_screenLocationTop = value; OnPropertyChanged("ScreenLocationTop");
                }
            }
        }
        property int32 ScreenLocationLeft
        {
            int32 get() { return m_screenLocationLeft; }
            void set(int32 value)
            {
                if (value != m_screenLocationLeft)
                {
                    m_screenLocationLeft = value; OnPropertyChanged("ScreenLocationLeft");
                }
            }
        }

        property float32 Latitude
        {
            float32 get() { return m_latlon.Latitude; }
        }

        property float32 Longitude
        {
            float32 get() { return m_latlon.Longitude; }
        }

        property bool IsSelected
        {
        internal:
            void set(bool value)
            {
                if (m_selected != value)
                {
                    m_selected = value; OnPropertyChanged("IsSelected");
                }
            };
        public:
            bool get() { return m_selected; }
        }

        property Platform::String^ ThumbnailImagePath
        {
            Platform::String^ get() { return m_thumbnailImagePath; }
            void set(Platform::String^ value)
            {
                if (m_thumbnailImagePath != value)
                {
                    m_thumbnailImagePath = value; OnPropertyChanged("ThumbnailImagePath");
                }
            }
        }

        property bool IsHomeLocation
        {
            bool get() { return m_homeForecast; }
            void set(bool value)
            {
                if (value != m_homeForecast)
                {
                    m_homeForecast = value; OnPropertyChanged("IsHomeLocation");
                }
            }
        }

    internal:
        explicit Forecast(const LatLon& location);

        property Platform::String^ UniqueIdentifier
        {
            Platform::String^ get()
            {
                return Locality + ", " + AdminDistrict + ", " + CountryRegion;
            }
        }

        property Platform::String^ AdminDistrict;
        property Platform::String^ CountryRegion;

        property DirectX::XMFLOAT4 SphereCoordinates
        {
            DirectX::XMFLOAT4 get() { return m_sphereCoordinates; }
        }

        property Windows::Foundation::DateTime MinDate
        {
        internal: Windows::Foundation::DateTime get() { return m_minDate; }
        private: void set(Windows::Foundation::DateTime value) { m_minDate = value; }
        }

        property Windows::Foundation::DateTime MaxDate
        {
        internal: Windows::Foundation::DateTime get() { return m_maxDate; }
        private: void set(Windows::Foundation::DateTime value) { m_maxDate = value; }
        }

        property std::vector<WeatherData^> Items
        {
            std::vector<WeatherData^> get()
            {
                return m_data;
            }
        }

        property ForecastGroup^ ParentGroup
        {
            ForecastGroup^ get()
            {
                return m_parentGroup.Resolve<ForecastGroup>();
            }

            void set(ForecastGroup^ value)
            {
                m_parentGroup = value;
            }
        }

        property LatLon Latlon
        {
            LatLon get() { return m_latlon; }
        }

        const std::vector<WeatherData^>& GetWeatherData() const
        {
            return m_data;
        }

        void UpdateWeatherData(const std::vector<WeatherData^>& weatherData);

        property utility::datetime LastCheckin;

        WeatherData^ GetForDate(Windows::Foundation::DateTime date) const;

    private:
        bool m_selected;
        bool m_homeForecast;
        bool m_pending;
        bool m_updateFailed;
        WeatherData^ m_current;
        Platform::WeakReference m_parentGroup;
        std::vector<WeatherData^> m_data;
        LatLon m_latlon;
        bool m_visible;
        Windows::Foundation::DateTime m_minDate;
        Windows::Foundation::DateTime m_maxDate;
        Platform::String^ m_locality;
        Platform::String^ m_displayName;
        DirectX::XMFLOAT4 m_sphereCoordinates;
        uint32_t m_uid;
        int32 m_screenLocationTop;
        int32 m_screenLocationLeft;
        Platform::String^ m_thumbnailImagePath;

        inline void OnPropertyChanged(Platform::String^ propertyName)
        {
            PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName));
        }
    };

}